#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<cassert>

const int gnum = 3;
struct end_point
{
    pid_t pid;
    int write_fd;
    end_point(pid_t id, int fd):pid(id), write_fd(fd)
    {}
    ~end_point(){}
};

void net_task()
{
    printf("%d 子进程，执行网络任务。。。\n", getpid());
}
void database_task()
{
    printf("%d 子进程，执行数据库任务。。。\n", getpid());
}
void log_task()
{
    printf("%d 子进程，执行日志任务。。。\n", getpid());
}

typedef void(*func)();
class Task
{
public:
    Task()
    {
        task.push_back(net_task);
        task.push_back(database_task);
        task.push_back(log_task);
    }
    void execute(int command)
    {
        task[command]();
    }
private:
    std::vector<func> task;
};


void execute_command()
{
    Task t;
    while(true)
    {
        int command;
        int n = read(0, &command, sizeof(int));
        if(n == sizeof(int))
        {
            t.execute(command);
        }
        else if(n == 0)
        {
            std::cout << "没有内容了，子进程退出了"<< getpid() << std::endl;
            break;
        }
    }
}
void CreateProcess(std::vector<end_point>& ep)
{
    std::vector<int> fd;
    for(int i = 0; i < gnum; ++i)
    {
        // 1.1 先创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n != 0)
        {
            perror("pipe()");
            break;
        }
        // 1.2 再创建子进程
        pid_t id = fork();
        if(id == 0) 
        {
            // 关闭子进程从父进程那继承过来的写端
            printf("%d子进程继承的写端：", getpid());
            for(auto it : fd) 
            {
                std::cout << it << " ";
                close(it);
            }
            printf("\n");
            /*子进程*/
            close(pipefd[1]);   // 子进程关闭写端
            dup2(pipefd[0], 0); // 我们希望子进程可以从输入流中读取命令，因为父进程在stdin中输入命令

            execute_command();  // 执行指令

            close(pipefd[0]);   // 用完都关闭
            exit(0);
        }

        // 1.3 父进程关闭读端
        close(pipefd[0]);
        ep.push_back(end_point(id, pipefd[1]));
        
        //存储父进程的写端fd，用于后续关闭子进程继承于父进程的写端
        fd.push_back(pipefd[1]);
    }
}
void AssignTask(std::vector<end_point>& ep)
{
    int index = 0;
    while(true)
    {
        // 2.1 选择任务 (随机)
        // int command = rand()%3;
        int command; //  (自选任务)
        printf("选择任务（0~2）:");
        std::cin >> command;
        if(command == -1) break;
        if(command < 0 || command >ep.size())
        continue;
        // 2.2 选择进程 (随机)
        // int pid = rand() % ep.size();
        int pid = index++ % ep.size();  //  (轮询)

        // 2.3 下发任务
        printf("第%d号--", pid);
        fflush(stdout);
        write(ep[pid].write_fd, &command, sizeof(command));
        sleep(1);
    }
}
void WaitProcess(std::vector<end_point>& ep)
{
    // // 1. 让子进程全部退出  -----  让父进程关闭所有写端
    // for(auto& it : ep) close(it.write_fd);
    // sleep(5);
    // // 2. 回收子进程，解除僵尸状态
    // for(auto& it : ep) waitpid(it.pid, nullptr, 0);
    // sleep(5);

    // 让子进程全部退出，回收子进程，解除僵尸状态
    for(auto& it : ep)
    {
        close(it.write_fd);
        waitpid(it.pid, nullptr, 0);
        sleep(3);
    } 
}

int main()
{
    // 1.先进行构建控制结构  父进程写入，子进程读取
    std::vector<end_point> ep;
    CreateProcess(ep);
    // 2. 父进程下发任务
    AssignTask(ep);
    // 3. 回收子进程
    WaitProcess(ep);
    return 0;
}